/*
 * GroceryStore.java
 *
 * Created on September 18, 2007, 3:26 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package grocerystore;

import java.util.ArrayList;
import java.util.Queue;
import java.util.Random;
import jsim.statistic.Histogram;
import jsim.statistic.SampleStat;
import jsim.variate.Exponential;
import simulationengine.event.Entity;
import simulationengine.event.Event;
import simulationengine.event.Scheduler;

/**
 * This class simulates a grocery store checkout counter as described in example 3.3
 * @author jkeister
 */
public class GroceryStore {
    
    static Scheduler scheduler;
    static SampleStat waitTimeStat;
    static SampleStat serviceTimeStat;
    static SampleStat totalTimeStat;
    static int numCallsCompleted = 0;
    static final int SIM_LENGTH = 100;
    
    static ArrayList<Entity> waitQueue = new ArrayList<Entity>();
        
    static boolean busy = false;
    static double serviceTime = 0;
    
    /** Creates a new instance of GroceryStore */
    public GroceryStore() 
    {
        scheduler = new Scheduler();
        waitTimeStat = new SampleStat("Time in queue");
        serviceTimeStat = new SampleStat("Time in service");
        totalTimeStat = new SampleStat("Time in system");
    }
    
    static class Arrival extends Event
    {
		Arrival(Entity call, double time)
		{
			super(call, time); 
		}
                
                public void occur()
		{
                    Random rand = new Random();
                    
                    double interArrivalTime = scheduler.getSystemTime();
                                                                               
			int randInt = rand.nextInt(999);
			if(randInt > 875 || randInt == 0)
				interArrivalTime += 8.0;
			else if (randInt > 750)
				interArrivalTime += 7.0;
			else if (randInt > 625)
				interArrivalTime += 6.0;
			else if (randInt > 500)
                                interArrivalTime += 5.0;
                        else if (randInt > 375)
                                interArrivalTime += 4.0;
                        else if (randInt > 250)
                                interArrivalTime += 3.0;
                        else if (randInt > 125)
                                interArrivalTime += 2.0;
                        else
                                interArrivalTime += 1.0;
                                                                    
                        scheduler.schedule(new Arrival(new Entity(), interArrivalTime));
                        
                        getEntity().setStartTime(scheduler.getSystemTime());
                        if(!busy)
                        {
                            
                            busy = true;
                            Random rand2 = new Random();
                                                        
                            serviceTime = 0.0;
                            int randInt2 = rand2.nextInt(99);
                            if(randInt2 > 95 || randInt2 == 0)
                                        serviceTime += 6.0;
                                else if (randInt2 > 85)
                                        serviceTime += 5.0;
                                else if (randInt2 > 60)
                                        serviceTime += 4.0;
                                else if (randInt2 > 30)
                                        serviceTime += 3.0;
                                else if (randInt2 > 10)
                                        serviceTime += 2.0;
                                else
                                        serviceTime += 1.0;
                            
                            scheduler.schedule(new Departure(getEntity(), serviceTime+scheduler.getSystemTime()));
                        }
                        else
                        {
                            waitQueue.add(getEntity());
                            //scheduler.schedule(getEntity)
                        }
                }
    }
            
    static class Departure extends Event
    {
		Departure(Entity call, double time)
		{
			super(call, time); 
		}
                
                public void occur()
		{
                    busy = false;
                    
                    serviceTimeStat.tally(serviceTime);
                    waitTimeStat.tally((scheduler.getSystemTime()-getEntity().getStartTime())-serviceTime);
                    
                    if(!waitQueue.isEmpty())
                    {
                        
                        busy = true;
                        Entity nextEntity = waitQueue.remove(0);
                        serviceTime = 0.0;
                                               
                        Random rand2 = new Random();
                            
                            int randInt2 = rand2.nextInt(99);
                            if(randInt2 > 95 || randInt2 == 0)
                                        serviceTime += 6.0;
                                else if (randInt2 > 85)
                                        serviceTime += 5.0;
                                else if (randInt2 > 60)
                                        serviceTime += 4.0;
                                else if (randInt2 > 30)
                                        serviceTime += 3.0;
                                else if (randInt2 > 10)
                                        serviceTime += 2.0;
                                else
                                        serviceTime += 1.0;
                            
                            scheduler.schedule(new Departure(nextEntity, serviceTime+scheduler.getSystemTime()));
                    }
                    
                    totalTimeStat.tally(scheduler.getSystemTime()-getEntity().getStartTime());
                    numCallsCompleted++;
                }
    }
    
    /**
     * This method starts the simulation and continues until finished
     */
    public static void run()
	{
		scheduler.schedule(new Arrival(new Entity(), 0.0));
		                
		while(numCallsCompleted < SIM_LENGTH)
		{
			scheduler.handle();
		}
		
		waitTimeStat.printStat();
                serviceTimeStat.printStat();
                totalTimeStat.printStat();
	}
    
    /**
     * Creates a new GroceryStore simulation and begins
     * @param args 
     */
    public static void main(String[] args) 
    {
        // TODO code application logic here
        GroceryStore gs = new GroceryStore();
		gs.run();
    }
    
}
